﻿using Manox.Libs.Foundation;
using Manox.Libs.Networking;
using System;


namespace Manox.Libs.ODB
{
    /// <summary>
    /// Object database property base.
    /// </summary>
    public abstract class ObjectDatabasePropertyBase
    {
        /// <summary>Name.</summary>
        internal string Name;
        /// <summary>Value changed.</summary>
        public event ObjectDatabasePropertyValueChanged ValueChanged;


        /// <summary>
        /// Method sets data.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="value"></param>
        public abstract void SetObject(object sender, object value);
       
        /// <summary>
        /// Method returns data.
        /// </summary>
        /// <returns></returns>
        public abstract object GetObject();

        /// <summary>
        /// Method calls ValueChanged event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="eventArgs"></param>
        public void CallValueChangedEvent(object sender, ObjectDatabasePropertyValueChangedEventArgs eventArgs)
        {
            if (this.ValueChanged != null)
                this.ValueChanged(sender, eventArgs);
        }

        /// <summary>
        /// Create event args.
        /// </summary>
        internal abstract ObjectDatabasePropertyValueChangedEventArgs CreateEventArgs(object sender);

        /// <summary>
        /// Force call valuye changed event.
        /// </summary>
        internal abstract void ForceCallValueChangedEvent(object sender);        
    }

    /// <summary>
    /// Object database property.
    /// </summary>
    public class ObjectDatabaseProperty<T> : ObjectDatabasePropertyBase
    {        
        /// <summary>Value.</summary>
        private T Value;


        /// <summary>
        /// Constructor.
        /// </summary>
        internal ObjectDatabaseProperty()
        {
            this.Value = default(T);
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        internal ObjectDatabaseProperty(T defaultValue)
        {
            this.Value = defaultValue;
        }

        /// <summary>
        /// Method sets data.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="value"></param>
        public override void SetObject(object sender, object value)
        {
            this.Set(sender, (T)value);
        }

        /// <summary>
        /// Method returns data.
        /// </summary>
        /// <returns></returns>
        public override object GetObject()
        {
            return this.Get();
        }

        /// <summary>
        /// Method sets data.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="value"></param>
        public void Set(object sender, T value)
        {
            if (!Object.Equals(this.Value, value))
            {
                T oldValue = this.Value;

                this.Value = value;

                this.ForceCallValueChangedEvent(sender, oldValue, this.Value);                
            }
        }

        /// <summary>
        /// Method returns data.
        /// </summary>
        /// <returns></returns>
        public T Get()
        {
            return this.Value;
        }

        /// <summary>
        /// Create event args.
        /// </summary>
        internal override ObjectDatabasePropertyValueChangedEventArgs CreateEventArgs(object sender)
        {
            return this.CreateEventArgs(sender, this.Value, this.Value);
        }

        /// <summary>
        /// Create event args.
        /// </summary>
        internal ObjectDatabasePropertyValueChangedEventArgs CreateEventArgs(object sender, T oldValue, T newValue)
        {
            ObjectDatabasePropertyValueChangedEventArgs eventArgs = new ObjectDatabasePropertyValueChangedEventArgs();
            eventArgs.PropertyName = this.Name;
            eventArgs.OldValue = oldValue;
            eventArgs.NewValue = newValue;

            return eventArgs;
        }

        /// <summary>
        /// Force call valuye changed event.
        /// </summary>
        internal override void ForceCallValueChangedEvent(object sender)
        {
            this.CallValueChangedEvent(sender, this.CreateEventArgs(sender));
        }

        /// <summary>
        /// Force call valuye changed event.
        /// </summary>
        internal void ForceCallValueChangedEvent(object sender, T oldValue, T newValue)
        {
            this.CallValueChangedEvent(sender, this.CreateEventArgs(sender, oldValue, newValue));
        }
    }


    /// <summary>
    /// Object database property value changed event args.
    /// </summary>
    public sealed class ObjectDatabasePropertyValueChangedEventArgs : ObjectDatabaseEventArgs
    {
        /// <summary>Property name.</summary>
        public string PropertyName;
        /// <summary>Old value.</summary>
        public object OldValue;
        /// <summary>New value.</summary>
        public object NewValue;


#if UNITY_NETWORK || STEAM_NETWORK
        /// <summary>
        /// Serialize.
        /// </summary>
        /// <param name="writer"></param>
        public override void Serialize(SerializeWriter writer)
        {
            base.Serialize(writer);

            writer.Write(this.PropertyName);
            writer.WriteObject(this.NewValue);
        }

        /// <summary>
        /// Deserialize.
        /// </summary>
        /// <param name="reader"></param>
        public override void Deserialize(SerializeReader reader)
        {
            base.Deserialize(reader);

            this.PropertyName = reader.ReadString();
            this.NewValue = reader.ReadObject();
        }
#endif                         
    }
   
    /// <summary>
    /// Object database property value changed.
    /// </summary>
    /// <param name="sender"></param>
    public delegate void ObjectDatabasePropertyValueChanged(object sender, ObjectDatabasePropertyValueChangedEventArgs eventArgs);
}
